#include "linux/module.h"
#include "linux/init.h"

#include "linux/kdev_t.h"
#include "linux/cdev.h"
#include "linux/fs.h"
#include "linux/uaccess.h"
#include "linux/io.h"

#define GPIO_DDR 0xFDD6000C
#define GPIO_DR 0xFDD60004

struct driver_test
{
    dev_t dev_num;         // 设备号
    struct cdev cdev_test; // cdev
    struct class *class;
    struct device *device;
    char Kbuf[32];
    unsigned int *vir_gpio_ddr;
    unsigned int *vir_gpio_dr;
} dev1;

// 打开设备函数
int led_open(struct inode *inode, struct file *file)
{
    file->private_data = &dev1;
    printk(KERN_INFO "打开\n");
    return 0;
}
// 关闭设备函数
int led_release(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "关闭\n");
    return 0;
}

// 向设备写入信息函数
ssize_t led_write(struct file *file, const char __user *buff, size_t size, loff_t *off)
{
    struct driver_test *test_dev = (struct driver_test *)file->private_data;
    if (copy_from_user(test_dev->Kbuf, buff, size) != 0)
    {
        printk(KERN_ERR "从用户空间复制数据失败\n");
        return -1;
    }
    if (test_dev->Kbuf[0] == 1) // 应用传送1开灯
    {
        *(test_dev->vir_gpio_dr) = 0x12403;
        printk(KERN_INFO "test_dev->kbuf[0]=%d\n", test_dev->Kbuf[0]);
    }
    else if (test_dev->Kbuf[0] == 0) ////应用传送0关灯
    {
        *(test_dev->vir_gpio_dr) = 0x12402;
        printk(KERN_INFO "test_dev->kbuf[0]=%d\n", test_dev->Kbuf[0]);
    }

    return 0;
}
// 从设备读取信息函数
ssize_t led_read(struct file *file, char __user *buff, size_t size, loff_t *off)
{
    struct driver_test *test_dev = (struct driver_test *)file->private_data;
    if (copy_to_user(buff, test_dev->Kbuf, strlen(test_dev->Kbuf)) != 0)
    {
        printk(KERN_ERR "从内和空间复制数据失败\n");
        return -1;
    }
    return 0;
}
struct file_operations cdev_test_fops = {
    .owner = THIS_MODULE,
    .open = led_open,
    .release = led_release,
    .write = led_write,
    .read = led_read,
};

static int __init led_init(void)
{
    int ret;
    // 自动申请设备号
    ret = alloc_chrdev_region(&dev1.dev_num, 0, 1, "LED_B");
    if (ret < 0)
    {
        goto err_chrdev;
    }
    printk(KERN_INFO "动态申请设备号成功,主设备号为:%d,次设备号为:%d\n", MAJOR(dev1.dev_num), MINOR(dev1.dev_num));
    // 设备初始化
    dev1.cdev_test.owner = THIS_MODULE;
    cdev_init(&dev1.cdev_test, &cdev_test_fops);
    // 设备添加
    ret = cdev_add(&dev1.cdev_test, dev1.dev_num, 1);
    if (ret < 0)
    {
        goto err_chr_add;
    }
    // 类创建
    dev1.class = class_create(THIS_MODULE, "LED");
    if (IS_ERR(dev1.class))
    {
        ret = PTR_ERR(dev1.class);
        goto err_class_create;
    }
    // 设备创建
    dev1.device = device_create(dev1.class, NULL, dev1.dev_num, NULL, "LED_B");
    if (IS_ERR(dev1.device))
    {
        ret = PTR_ERR(dev1.device);
        goto err_device_create;
    }
    // 地址映射
    dev1.vir_gpio_ddr = ioremap(GPIO_DDR, 4);
    if (IS_ERR(dev1.vir_gpio_ddr))
    {
        ret = PTR_ERR(dev1.vir_gpio_ddr);
        goto err_ioremap;
    }
    dev1.vir_gpio_dr = ioremap(GPIO_DR, 4);
    if (IS_ERR(dev1.vir_gpio_dr))
    {
        ret = PTR_ERR(dev1.vir_gpio_dr);
        goto err_ioremap;
    }
    //方向寄存器
    *(dev1.vir_gpio_ddr)= 0x130C3;
    return 0;

err_ioremap:
    iounmap(dev1.vir_gpio_dr);
    iounmap(dev1.vir_gpio_ddr);
err_device_create:
    device_destroy(dev1.class, dev1.dev_num);
err_class_create:
    class_destroy(dev1.class);
err_chr_add:
    unregister_chrdev_region(dev1.dev_num, 1);
err_chrdev:
    return ret;
}

static void __exit led_exit(void)
{
    iounmap(dev1.vir_gpio_dr);
    iounmap(dev1.vir_gpio_ddr);
    device_destroy(dev1.class, dev1.dev_num);
    class_destroy(dev1.class);
    unregister_chrdev_region(dev1.dev_num, 1);
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("逮就捕");